//
// Created by Alex on 2021/9/17.
//


#include "utils.h"
#include <cstdio>
#include <utility>

using namespace std;

int n_preserved_lex = 12;
string preserved_lex[] = {"main", "const", "int", "break", "continue", "if",
                               "else", "while", "getint", "printf", "return", "void"};

std::set<std::string> preserved::lex_set;


/*
 * lexical part
 */
lexical_parse::lexical_parse(int line_number, string text, string type_code) {
    this->line_number = line_number;
    this->text = text;
    this->type_code = type_code;
    this->output = this->type_code + " " + this->text;
}

lexical_parse::lexical_parse() {}

lexical_parse::lexical_parse(lexical_parse const &o) {
    this->line_number = o.line_number;
    this->text = o.text;
    this->type_code = o.type_code;
    this->output = this->type_code + " " + this->text;
}

void lexical_parse::print(FILE *const stream) const {
    fprintf(stream, "%s\n", output.c_str());
}

preserved::preserved() {
    if (!lex_set.empty()) return;
    for (int i = 0; i < n_preserved_lex; i++)
        lex_set.insert(preserved_lex[i]);
}

bool preserved::is_preserved(const string& v) {
    return lex_set.count(v) > 0;
}

string preserved::to_token(const string& v) {
    string ret(v);
    transform(v.begin(), v.end(), ret.begin(), (int(*)(int))toupper);
    return ret + "TK";
}

void lexical_result::print_to_file(char *output_filename) {
    FILE *fo = fopen(output_filename, "w");
    printf("see lexical analysis result in %s\n", output_filename);
    if (this->success)
        for (const lexical_parse& lp : this->parses)
            fprintf(fo, "%s\n", lp.output.c_str());
    fclose(fo);
}

/*
 * syntax part
 */

syntax_result::syntax_result(CompUnit *_comp_unit, std::vector<Error> *_error):
comp_unit(_comp_unit), error(_error), success(!_error || _error->empty()){ }

nonterminal::nonterminal(NonTermlType type): obj_type(type) {}

CompUnit::CompUnit(): nonterminal(COMPUNIT), Scope((Scope*)nullptr) {}

void CompUnit::add_content(vector<Decl*> *_decls, vector<FuncDef*> *_func_defs,
                           MainFuncDef *_main_func_def){
    decls = _decls;
    func_defs = _func_defs;
    main_func_def = _main_func_def;
}

Decl::Decl(NonTermlType type): BlockItem(type) {}

BlockItem::BlockItem(NonTermlType type): nonterminal(type) {}

ConstDecl::ConstDecl(lexical_parse* _const_tk, lexical_parse* _btype, ConstDef *_const_def,
vector<pair<lexical_parse*, ConstDef*>*> *_const_defs, lexical_parse* _semicn):
const_tk(_const_tk), btype(_btype), const_def(_const_def),
const_defs(_const_defs), semicn(_semicn) ,Decl(CONSTDECL) {
    real_const_defs = new vector<ConstDef*>();
    real_const_defs->push_back(_const_def);
    for (auto d : *_const_defs) {
        real_const_defs->push_back(d->second);
    }
}

ConstExp::ConstExp(AddExp *_add_exp): add_exp(_add_exp), nonterminal(CONSTEXP) {}

ConstInitVal::ConstInitVal(ConstExp *_const_exp):const_exp(_const_exp), nonterminal(CONSTINITVAL) {}

ConstInitVal::ConstInitVal(lexical_parse *_lb, lexical_parse* _rb):
lb(_lb), rb(_rb) , nonterminal(CONSTINITVAL){}

ConstInitVal::ConstInitVal(lexical_parse *_lb, ConstInitVal* _const_init_val,
                           std::vector<std::pair<lexical_parse*, ConstInitVal*>*> *_const_init_vals,
                           lexical_parse* _rb) :
    lb(_lb), const_init_val(_const_init_val),
    const_init_vals(_const_init_vals), rb(_rb) , nonterminal(CONSTINITVAL){
    real_init_vals = new vector<ConstInitVal*>();
    real_init_vals->push_back(const_init_val);
    for (auto d : *_const_init_vals) {
        real_init_vals->push_back(d->second);
    }
}

ConstDef::ConstDef(lexical_parse* _ident, vector<std::tuple<lexical_parse*, ConstExp*, lexical_parse*>*> *_exps,
lexical_parse* _assign, ConstInitVal *_const_init_val):
ident(_ident), exps(_exps), assign(_assign), const_init_val(_const_init_val), nonterminal(CONSTDEF) {}

AddExp::AddExp(AddExp *_add_exp, lexical_parse* _plus_minus, MulExp *_mul_exp):
add_exp(_add_exp), opr(_plus_minus), mul_exp(_mul_exp), nonterminal(ADDEXP) {}

AddExp::AddExp(MulExp *_mul_exp):
add_exp(nullptr), mul_exp(_mul_exp), nonterminal(ADDEXP) {}

MulExp::MulExp(MulExp *_mul_exp, lexical_parse* _opr, UnaryExp *_unary_exp):
mul_exp(_mul_exp), opr(_opr), unary_exp(_unary_exp), nonterminal(MULEXP) {}

MulExp::MulExp(UnaryExp *_unary_exp):
mul_exp(nullptr), unary_exp(_unary_exp) , nonterminal(MULEXP) {}

UnaryExp::UnaryExp(PrimaryExp *_primary_exp):
primary_exp(_primary_exp) , nonterminal(UNARYEXP) {}

UnaryExp::UnaryExp(lexical_parse* _ident, lexical_parse* _lp, lexical_parse* _rp, FuncSym * _func_sym) :
ident(_ident), lp(_lp), rp(_rp), func_sym(_func_sym), nonterminal(UNARYEXP) {}

UnaryExp::UnaryExp(lexical_parse* _ident, lexical_parse* _lp,
    FuncRParams *_func_rparams, lexical_parse* _rp, FuncSym * _func_sym) :
    ident(_ident), lp(_lp), func_rparams(_func_rparams),
    rp(_rp), func_sym(_func_sym) , nonterminal(UNARYEXP){}

UnaryExp::UnaryExp(UnaryOp *_unary_op, UnaryExp *_unary_exp) :
unary_op(_unary_op), unary_exp(_unary_exp), nonterminal(UNARYEXP) {}

PrimaryExp::PrimaryExp(lexical_parse* _lp, Exp *_exp, lexical_parse* _rp):
lp(_lp), exp(_exp), rp(_rp) , nonterminal(PRIMARYEXP){}

PrimaryExp::PrimaryExp(LVal *_lval): lval(_lval) , nonterminal(PRIMARYEXP){}

PrimaryExp::PrimaryExp(Number *_number): number(_number) , nonterminal(PRIMARYEXP){}

FuncRParams::FuncRParams(Exp *_exp, std::vector<std::pair<lexical_parse*, Exp*>*> *_exps):
exp(_exp), exps(_exps), nonterminal(FUNCRPARAMS) {
    real_exps = new vector<Exp*>();
    real_exps->push_back(exp);
    for (auto p : *exps) {
        real_exps->push_back(p->second);
    }
}

Exp::Exp(AddExp *_add_exp) : add_exp(_add_exp), nonterminal(EXP) {}

VarDecl::VarDecl(lexical_parse* _btype, VarDef *_var_def, std::vector<std::pair<lexical_parse*, VarDef *>*> *_var_defs,
                 lexical_parse* _semicn):
    btype(_btype), var_def(_var_def), var_defs(_var_defs),
    semicn(_semicn), Decl(VARDECL) {
    real_var_defs = new vector<VarDef*>();
    real_var_defs->push_back(_var_def);
    for (auto d : *var_defs) {
        real_var_defs->push_back(d->second);
    }
}

VarDef::VarDef(lexical_parse* _ident, std::vector<std::tuple<lexical_parse*, ConstExp *, lexical_parse*>*> *_exps):
ident(_ident), exps(_exps), nonterminal(VARDEF) {}

VarDef::VarDef(lexical_parse* _ident, std::vector<std::tuple<lexical_parse*, ConstExp *, lexical_parse*>*> *_exps,
lexical_parse* _assign, InitVal *_init_val):
ident(_ident), exps(_exps), assign(_assign), init_val(_init_val) , nonterminal(VARDEF){}

InitVal::InitVal(Exp* _exp): exp(_exp) , nonterminal(INITVAL){}

InitVal::InitVal(lexical_parse* _lb, lexical_parse* _rb): lb(_lb), rb(_rb) , nonterminal(INITVAL){}

InitVal::InitVal(lexical_parse* _lb, InitVal *_init_val, std::vector<std::pair<lexical_parse*, InitVal *>*> *_init_vals,
lexical_parse* _rb):
lb(_lb), init_val(_init_val), init_vals(_init_vals), rb(_rb) , nonterminal(INITVAL){}

FuncDef::FuncDef(FuncType *_func_type, lexical_parse* _ident, lexical_parse* _lp, FuncFParams *_func_fparams,
                 lexical_parse* _rp):
    func_type(_func_type), ident(_ident), lp(_lp), func_fparams(_func_fparams),
    rp(_rp), nonterminal(FUNCDEF) {}

FuncDef::FuncDef(FuncType *_func_type, lexical_parse* _ident, lexical_parse* _lp,
lexical_parse* _rp):
func_type(_func_type), ident(_ident), lp(_lp), rp(_rp), nonterminal(FUNCDEF){}

void FuncDef::add_content(Block *_block) { block = _block; }

FuncDef::FuncDef(FuncDef* def): func_type(def->func_type), ident(def->ident),
lp(def->lp), func_fparams(def->func_fparams), rp(def->rp), block(def->block) ,
nonterminal(MAINFUNCDEF){}

MainFuncDef::MainFuncDef(FuncDef *main_func_def):
FuncDef(main_func_def), btype(func_type->btype){}

FuncFParams::FuncFParams(FuncFParam *_func_fparam, std::vector<std::pair<lexical_parse*, FuncFParam *>*> *_func_fparams) :
func_fparam(_func_fparam), func_fparams(_func_fparams) , nonterminal(FUNCFPARAMS){
    real_fparams = new vector<FuncFParam*>();
    real_fparams->push_back(_func_fparam);
    for (auto d : *_func_fparams) {
        real_fparams->push_back(d->second);
    }
}

FuncFParam::FuncFParam(lexical_parse* _btype, lexical_parse* _ident, lexical_parse* _lb, lexical_parse* _rb,
vector<std::tuple<lexical_parse*, ConstExp *, lexical_parse*>*> *_exps):
nonterminal(FUNCFPARAM), btype(_btype), ident(_ident), lb(_lb), rb(_rb), exps(_exps) {}

FuncFParam::FuncFParam(lexical_parse* _btype, lexical_parse* _ident):
btype(_btype), ident(_ident),nonterminal(FUNCFPARAM) {}

Block::Block(Scope *parent) : nonterminal(BLOCK), Scope(parent) {}

void Block::add_content(lexical_parse* _lb, std::vector<BlockItem*> *_block_items, lexical_parse* _rb) {
    lb = _lb;
    block_items = _block_items;
    rb = _rb;
}

UnaryOp::UnaryOp(lexical_parse* _opr) :nonterminal(UNARYOP),  opr(_opr) {}

LVal::LVal(lexical_parse* _ident, std::vector<std::tuple<lexical_parse*, Exp *, lexical_parse*>*> *_exps, ValueSym *_value_sym):
nonterminal(LVAL), ident(_ident), exps(_exps), value_sym(_value_sym) { }

Number::Number(lexical_parse* _int_const) :nonterminal(NUMBER),  int_const(_int_const) {}


FuncType::FuncType(lexical_parse* _btype) : nonterminal(FUNCTYPE), btype(_btype) {}

AssignStmt::AssignStmt(LVal *_lval, lexical_parse* _assign, Exp *_exp, lexical_parse* _semicn) :
nonterminal(ASSIGNSTMT), lval(_lval), assign(_assign), semicn(_semicn), exp(_exp) {}

AssignStmt::AssignStmt(LVal *_lval, lexical_parse* _assign, lexical_parse* _getint,
lexical_parse* _lp, lexical_parse* _rp, lexical_parse* _semicn) :
nonterminal(ASSIGNSTMT), lval(_lval), assign(_assign), semicn(_semicn), getint(_getint),
lp(_lp), rp(_rp) {}

SelectiveExp::SelectiveExp(Exp *_exp, lexical_parse* _semicn) :
nonterminal(SELECTIVEEXP), exp(_exp), semicn(_semicn) {}

SelectiveExp::SelectiveExp(lexical_parse* _semicn):nonterminal(SELECTIVEEXP),semicn(_semicn) {}

IfStmt::IfStmt(lexical_parse* _if_tk, lexical_parse* _lp, Cond *_cond, lexical_parse* _rp, Stmt *_if_true):
nonterminal(IFSTMT),if_tk(_if_tk), lp(_lp), cond(_cond), rp(_rp), if_true(_if_true) {}

IfStmt::IfStmt(lexical_parse* _if_tk, lexical_parse* _lp, Cond *_cond, lexical_parse* _rp, Stmt *_if_true,
lexical_parse* _else_tk, Stmt *_if_else):
nonterminal(IFSTMT),if_tk(_if_tk), lp(_lp), cond(_cond), rp(_rp),
if_true(_if_true), else_tk(_else_tk), if_else(_if_else) {}

WhileStmt::WhileStmt(lexical_parse* _while_tk, lexical_parse* _lp, Cond *_cond, lexical_parse* _rp, Stmt *_stmt):
nonterminal(WHILESTMT), while_tk(_while_tk), lp(_lp), cond(_cond), rp(_rp), stmt(_stmt) {}

BreakStmt::BreakStmt(lexical_parse* _break_tk, lexical_parse* _semicn, Stmt *_outer) :
nonterminal(BREAKSTMT),break_tk(_break_tk), semicn(_semicn), outer(_outer) {}

ContinueStmt::ContinueStmt(lexical_parse* _continue_tk, lexical_parse* _semicn, Stmt *_outer) :
nonterminal(CONTINUESTMT),continue_tk(_continue_tk), semicn(_semicn) , outer(_outer) {}

ReturnStmt::ReturnStmt(lexical_parse* _return_tk, lexical_parse* _semicn):
nonterminal(RETURNSTMT),return_tk(_return_tk), semicn(_semicn) {}

ReturnStmt::ReturnStmt(lexical_parse* _return_tk, Exp*_exp, lexical_parse* _semicn):
nonterminal(RETURNSTMT),return_tk(_return_tk), exp(_exp), semicn(_semicn) {}

PrintfStmt::PrintfStmt(lexical_parse* _printf_tk, lexical_parse* _lp, lexical_parse* _format_string,
std::vector<std::pair<lexical_parse*, Exp *>*> *_exps, lexical_parse* _rp, lexical_parse* _semicn) :
nonterminal(PRINTFSTMT),printf_tk(_printf_tk), lp(_lp), format_string(_format_string),
exps(_exps), rp(_rp), semicn(_semicn) {}

Stmt::Stmt(): BlockItem(STMT) {}
Stmt::Stmt(AssignStmt *_stmt):assign_stmt(_stmt), BlockItem(STMT) {}
Stmt::Stmt(SelectiveExp *_stmt):selective_exp(_stmt), BlockItem(STMT) {}
Stmt::Stmt(Block *_stmt):block(_stmt), BlockItem(STMT) {}
Stmt::Stmt(IfStmt *_stmt):if_stmt(_stmt), BlockItem(STMT) {}
Stmt::Stmt(WhileStmt *_stmt):while_stmt(_stmt), BlockItem(STMT) {}
Stmt::Stmt(BreakStmt *_stmt):break_stmt(_stmt), BlockItem(STMT) {}
Stmt::Stmt(ContinueStmt *_stmt):continue_stmt(_stmt), BlockItem(STMT) {}
Stmt::Stmt(ReturnStmt *_stmt):return_stmt(_stmt), BlockItem(STMT) {}
Stmt::Stmt(PrintfStmt *_stmt):printf_stmt(_stmt), BlockItem(STMT) {}

void Stmt::add_content(nonterminal *stmt) {
    if (stmt->obj_type == WHILESTMT) {
        while_stmt = (WhileStmt*)stmt;
    } else {
        fprintf(stderr, "unknown situation when adding content to a stmt\n");
    }
}

Cond::Cond(LOrExp *_lor_exp) :nonterminal(COND), lor_exp(_lor_exp) {}

LOrExp::LOrExp(LOrExp *_lor_exp, lexical_parse* _or_op, LAndExp *_land_exp) :
nonterminal(LOREXP),lor_exp(_lor_exp), or_op(_or_op), land_exp(_land_exp) {}

LOrExp::LOrExp(LAndExp *_land_exp) : nonterminal(LOREXP),land_exp(_land_exp) {}


LAndExp::LAndExp(EqExp *_eq_exp):nonterminal(LANDEXP), eq_exp(_eq_exp) {}

LAndExp::LAndExp(LAndExp *_land_exp, lexical_parse* _and_op, EqExp *_eq_exp):
nonterminal(LANDEXP),land_exp(_land_exp), and_op(_and_op), eq_exp(_eq_exp) {}

EqExp::EqExp(RelExp *_rel_exp) : nonterminal(EQEXP),rel_exp(_rel_exp) {}

EqExp::EqExp(EqExp *_eq_exp, lexical_parse* _opr, RelExp *_rel_exp) :
nonterminal(EQEXP),eq_exp(_eq_exp), opr(_opr), rel_exp(_rel_exp) {}

RelExp::RelExp(AddExp *_add_exp): nonterminal(RELEXP),add_exp(_add_exp) {}

RelExp::RelExp(RelExp *_rel_exp, lexical_parse* _opr, AddExp *_add_exp) :
nonterminal(RELEXP),rel_exp(_rel_exp), opr(_opr), add_exp(_add_exp) {}

void syntax_result::print(char* output_filename, char *error_filename) {
    FILE *out;
    if (success) {
        out = fopen(output_filename, "w");
    } else {
        out = fopen(error_filename, "w");
    }
    print(out);
    fclose(out);
}

void syntax_result::print(FILE *const stream) {
    if (success) { comp_unit->print(stream); }
    else {
        std::sort(error->begin(), error->end());
        for (auto e : *error) {
            e.print(stream);
        }
    }
}

void CompUnit::print(FILE *const stream) {
    for (auto e : *decls)
        e->print(stream);
    for (auto e : *func_defs)
        e->print(stream);
    main_func_def->print(stream);
    fprintf(stream, "<CompUnit>\n");
}

void FuncDef::print(FILE *const stream) {
    func_type->print(stream);
    ident->print(stream);
    lp->print(stream);
    if (func_fparams)
        func_fparams->print(stream);
    rp->print(stream);
    block->print(stream);
    fprintf(stream, "<FuncDef>\n");
}

void MainFuncDef::print(FILE *const stream) {
    btype->print(stream);
    ident->print(stream);
    lp->print(stream);
    rp->print(stream);
    block->print(stream);
    fprintf(stream, "<MainFuncDef>\n");
}

void FuncFParams::print(FILE *const stream) {
    func_fparam->print(stream);
    for (auto e : * func_fparams) {
        e->first->print(stream);
        e->second->print(stream);
    }
    fprintf(stream, "<FuncFParams>\n");
}

void FuncFParam::print(FILE *const stream) {
    btype->print(stream);
    ident->print(stream);
    if (lb) {
        lb->print(stream);
        rb->print(stream);
        for (auto e : *exps) {
            get<0>(*e)->print(stream);
            get<1>(*e)->print(stream);
            get<2>(*e)->print(stream);
        }
    }
    fprintf(stream, "<FuncFParam>\n");
}

void FuncType::print(FILE *const stream) {
    btype->print(stream);
    fprintf(stream, "<FuncType>\n");
}

void Stmt::print(FILE *const stream) {
    if (assign_stmt) assign_stmt->print(stream);
    else if (selective_exp) selective_exp->print(stream);
    else if (block) block->print(stream);
    else if (if_stmt) if_stmt->print(stream);
    else if (while_stmt) while_stmt->print(stream);
    else if (break_stmt) break_stmt->print(stream);
    else if (continue_stmt) continue_stmt->print(stream);
    else if (return_stmt) return_stmt->print(stream);
    else  printf_stmt->print(stream);
    fprintf(stream, "<Stmt>\n");
}

void AssignStmt::print(FILE *const stream) {
    lval->print(stream);
    assign->print(stream);
    if (exp) {
        exp->print(stream);
    } else {
        getint->print(stream);
        lp->print(stream);
        rp->print(stream);
    }
    semicn->print(stream);
}

void SelectiveExp::print(FILE *const stream) {
    if (exp)
        exp->print(stream);
    semicn->print(stream);
}

void IfStmt::print(FILE *const stream) {
    if_tk->print(stream);
    lp->print(stream);
    cond->print(stream);
    rp->print(stream);
    if_true->print(stream);
    if (else_tk) {
        else_tk->print(stream);
        if_else->print(stream);
    }
}

void Block:: print(FILE *const stream) {
    lb->print(stream);
    for (auto e : *block_items)
        e->print(stream);
    rb->print(stream);
    fprintf(stream, "<Block>\n");
}

void WhileStmt::print(FILE *const stream) {
    while_tk->print(stream);
    lp->print(stream);
    cond->print(stream);
    rp->print(stream);
    stmt->print(stream);
}

void BreakStmt::print(FILE *const stream) {
    break_tk->print(stream);
    semicn->print(stream);
}

void ContinueStmt::print(FILE *const stream) {
    continue_tk->print(stream);
    semicn->print(stream);
}

void ReturnStmt::print(FILE *const stream) {
    return_tk->print(stream);
    if (exp)
        exp->print(stream);
    semicn->print(stream);
}

void PrintfStmt::print(FILE *const stream) {
    printf_tk->print(stream);
    lp->print(stream);
    format_string->print(stream);
    for (auto e : *exps) {
        e->first->print(stream);
        e->second->print(stream);
    }
    rp->print(stream);
    semicn->print(stream);
}

void ConstDecl::print(FILE *const stream) {
    const_tk->print(stream);
    btype->print(stream);
    const_def->print(stream);
    for (auto e : *const_defs) {
        e->first->print(stream);
        e->second->print(stream);
    }
    semicn->print(stream);
    fprintf(stream, "<ConstDecl>\n");
}

void ConstDef::print(FILE *const stream) {
    ident->print(stream);
    for (auto e : * exps) {
        get<0>(*e)->print(stream);
        get<1>(*e)->print(stream);
        get<2>(*e)->print(stream);
    }
    assign->print(stream);
    const_init_val->print(stream);
    fprintf(stream, "<ConstDef>\n");
}

void ConstExp::print(FILE *const stream) {
    add_exp->print(stream);
    fprintf(stream, "<ConstExp>\n");
}

void ConstInitVal::print(FILE *const stream) {
    if (const_exp) {
        const_exp->print(stream);
    } else {
        lb->print(stream);
        if (const_init_val) {
            const_init_val->print(stream);
            for (auto e : *const_init_vals) {
                e->first->print(stream);
                e->second->print(stream);
            }
        }
        rb->print(stream);
    }
    fprintf(stream, "<ConstInitVal>\n");
}

void AddExp::print(FILE *const stream) {
    if (add_exp) {
        add_exp->print(stream);
        opr->print(stream);
    }
    mul_exp->print(stream);
    fprintf(stream, "<AddExp>\n");
}

void MulExp::print(FILE *const stream) {
    if (mul_exp) {
        mul_exp->print(stream);
        opr->print(stream);
    }
    unary_exp->print(stream);
    fprintf(stream, "<MulExp>\n");
}

void UnaryExp::print(FILE *const stream) {
    if (primary_exp) {
        primary_exp->print(stream);
    } else if (ident) {
        ident->print(stream);
        lp->print(stream);
        if (func_rparams)
            func_rparams->print(stream);
        rp->print(stream);
    } else {
        unary_op->print(stream);
        unary_exp->print(stream);
    }
    fprintf(stream, "<UnaryExp>\n");
}

void UnaryOp::print(FILE *const stream) {
    opr->print(stream);
    fprintf(stream, "<UnaryOp>\n");
}

void PrimaryExp::print(FILE *const stream) {
    if (lp) {
        lp->print(stream);
        exp->print(stream);
        rp->print(stream);
    } else if (lval) {
        lval->print(stream);
    } else {
        number->print(stream);
    }
    fprintf(stream, "<PrimaryExp>\n");
}

void LVal::print(FILE *const stream) {
    ident->print(stream);
    for (auto e : *exps) {
        get<0>(*e)->print(stream);
        get<1>(*e)->print(stream);
        get<2>(*e)->print(stream);
    }
    fprintf(stream, "<LVal>\n");
}

void Number::print(FILE *const stream) {
    int_const->print(stream);
    fprintf(stream, "<Number>\n");
}

void FuncRParams::print(FILE *const stream) {
    exp->print(stream);
    for (auto e : *exps) {
        e->first->print(stream);
        e->second->print(stream);
    }
    fprintf(stream, "<FuncRParams>\n");
}

void Exp::print(FILE *const stream) {
    add_exp->print(stream);
    fprintf(stream, "<Exp>\n");
}

void VarDecl::print(FILE *const stream) {
    btype->print(stream);
    var_def->print(stream);
    for (auto e : *var_defs) {
        e->first->print(stream);
        e->second->print(stream);
    }
    semicn->print(stream);
    fprintf(stream, "<VarDecl>\n");
}

void VarDef::print(FILE *const stream) {
    ident->print(stream);
    for (auto e : *exps) {
        get<0>(*e)->print(stream);
        get<1>(*e)->print(stream);
        get<2>(*e)->print(stream);
    }
    if (assign) {
        assign->print(stream);
        init_val->print(stream);
    }
    fprintf(stream, "<VarDef>\n");
}

void InitVal::print(FILE *const stream) {
    if (exp) {
        exp->print(stream);
    } else {
        lb->print(stream);
        if (init_val) {
            init_val->print(stream);
            for (auto e : *init_vals) {
                e->first->print(stream);
                e->second->print(stream);
            }
        }
        rb->print(stream);
    }
    fprintf(stream, "<InitVal>\n");
}

void Cond::print(FILE *const stream) {
    lor_exp->print(stream);
    fprintf(stream, "<Cond>\n");
}

void LOrExp::print(FILE *const stream) {
    if (lor_exp) {
        lor_exp->print(stream);
        or_op->print(stream);
    }
    land_exp->print(stream);
    fprintf(stream, "<LOrExp>\n");
}

void LAndExp::print(FILE *const stream) {
    if (land_exp) {
        land_exp->print(stream);
        and_op->print(stream);
    }
    eq_exp->print(stream);
    fprintf(stream, "<LAndExp>\n");
}

void EqExp::print(FILE *const stream) {
    if (eq_exp) {
        eq_exp->print(stream);
        opr->print(stream);
    }
    rel_exp->print(stream);
    fprintf(stream, "<EqExp>\n");
}

void RelExp::print(FILE *const stream) {
    if (rel_exp) {
        rel_exp->print(stream);
        opr->print(stream);
    }
    add_exp->print(stream);
    fprintf(stream, "<RelExp>\n");
}

Error::Error(int _line, char _type): line(_line), type(_type) {}

void Error::print(FILE *const stream) const { fprintf(stream, "%d %c\n", line, type); }

bool Error::operator<(const Error & o) const { return line < o.line; }

size_t str_hash::operator()(const string& str) const {
    hash<string> h;
    return h(str);
}

Scope::Scope(Scope *_parent): parent(_parent), symtab(new Symtab()) {}

Symbol* Scope::lookup(const string& name, SymType type) const {
    auto ret = symtab->lookup(name, type);
    if (!ret && parent) {
        return parent->lookup(name, type);
    }
    return ret;
}

Symtab::Symtab() = default;

bool Symtab::enter(Symbol *sym) {
    if (sym->type == VALUESYM) {
        if (var_tab.count(sym->name) > 0)
            return false;
        var_tab[sym->name] = sym;
    } else {   // FUNC
        if (func_tab.count(sym->name) > 0)
            return false;
        func_tab[sym->name] = sym;
    }
    return true;
}

Symbol* Symtab::lookup(const string& name, SymType type) {
    switch (type) {
        case VALUESYM:
            if (var_tab.count(name) == 0)
                return nullptr;
            else
                return var_tab[name];
        case FUNCSYM:
            if (func_tab.count(name) == 0)
                return nullptr;
            else
                return func_tab[name];
    }
    return nullptr;
}

Symbol::Symbol(string _name, SymType _type, nonterminal *_def):
name(std::move(_name)), type(_type), def(_def) {}

ValueSym::ValueSym(ConstDef *def):
Symbol(def->ident->text, VALUESYM, def), is_const(true) {
    dimension = (int)(def->exps->size());
}

ValueSym::ValueSym(VarDef *def):
Symbol(def->ident->text, VALUESYM, def) {
    dimension = (int)(def->exps->size());
}

ValueSym::ValueSym(FuncFParam *def):
Symbol(def->ident->text, VALUESYM, def), is_param(true) {
    dimension = def->get_dimension();
}

FuncSym::FuncSym(FuncDef *def):
Symbol(def->ident->text, FUNCSYM, def) {
    ret_type = (def->func_type->btype->type_code == "VOIDTK") ? VOID : INT;
}

int Exp::get_dimension() {
    if (dimension == DIMENSION_NOT_GET)
        dimension = add_exp->get_dimension();
    return dimension;
}

int AddExp::get_dimension() {
    if (dimension == DIMENSION_NOT_GET) {
        dimension = mul_exp->get_dimension();
        if (add_exp) {
            int ad = add_exp->get_dimension();
            if (ad == DIMENSION_ERROR || dimension == DIMENSION_ERROR) {
                dimension = DIMENSION_ERROR;
            } else if (dimension == 0 || ad == 0) {
                dimension = max(dimension, ad);
            } else {
                dimension = DIMENSION_ERROR;
                fprintf(stderr, "wrong AddExp at line %d\n", opr->line_number);
            }
        }
    }
    return dimension;
}

int MulExp::get_dimension() {
    if (dimension == DIMENSION_NOT_GET) {
        dimension = unary_exp->get_dimension();
        if (mul_exp) {
            int md = mul_exp->get_dimension();
            if (md == DIMENSION_ERROR || dimension == DIMENSION_ERROR) {
                dimension = DIMENSION_ERROR;
            } else if (md != 0 || dimension != 0) {
                dimension = DIMENSION_ERROR;
                fprintf(stderr, "wrong MulExp at line %d\n", opr->line_number);
            }
        }
    }
    return dimension;
}

int UnaryExp::get_dimension() {
    if (dimension == DIMENSION_NOT_GET) {
        if (primary_exp) {
            dimension = primary_exp->get_dimension();
        } else if (ident) {
            if (func_sym && func_sym->ret_type == INT) {
                dimension = 0;
            } else {
                fprintf(stderr, "try to get dimension on void func at line %d\n", ident->line_number);
                dimension = DIMENSION_ERROR;
            }
        } else {      // unary exp
            dimension = unary_exp->get_dimension();
            if (dimension > 0) {
                dimension = DIMENSION_ERROR;
                fprintf(stderr, "wrong unary op at line %d\n", unary_exp->get_line());
            }
        }
    }
    return dimension;
}

int PrimaryExp::get_dimension() {
    if (dimension == DIMENSION_NOT_GET) {
        if (exp) {
            dimension = exp->get_dimension();
        } else if (lval) {
            dimension = lval->get_dimension();
        } else {      // number
            dimension = 0;
        }
    }
    return dimension;
}

int Number::get_dimension() { return 0; }

int LVal::get_dimension() {
    if (dimension == DIMENSION_NOT_GET) {
        if (value_sym) {
            dimension = value_sym->dimension - (int)(exps->size());
            if (dimension < 0) {
                dimension = DIMENSION_ERROR;
                fprintf(stderr, "invalid access to array at line %d\n", ident->line_number);
            }
        } else {
            fprintf(stderr, "try to get the dimension of ident that wasn't declared at line %d\n", ident->line_number);
            dimension = DIMENSION_ERROR;  /// undefined sym
        }
    }
    return dimension;
}

// here dim = exp.sz + 1
int FuncFParam::get_dimension() {
    if (dimension == DIMENSION_NOT_GET) {
        if (!exps) {
            dimension = 0;
        } else {
            dimension = (int)(exps->size()) + 1;
        }
    }
    return dimension;
}
